<?php
/**
* This class takes a Weather.com city ID and parses all data in the RSS feed of weather.com into a readable array
*
* @author Dieter Verjans <dieter@inventis.be>
* @copyright 2009 Inventis BVBA
* @license http://www.gnu.org/licenses/gpl.html
*
*/
class Inventis_Weathercomforecast
{
    
    /**
     * config vars
     */
    private $cityId;
    private $units = 'm';    
    private $dayLength = 5;
    private $cacheLifetime = 3600;
    private $partnerId = '-- your Weather.com PartnerID --';
    private $licenseKey= '-- your Weather.com LicenseKey --';
    private $cachePath = '-- path to the cache directory --';
    
    /**
     * XML Vars
     */
    private $xmlLocation;
    private $xmlData;
    private $xmlString;
    private $xmlParser;
    private $xmlUrl;
    
    /**
     * Constructor
     *
     * @param string $cityId
     * @param int $dayLength (max 5: today + 4 next days)    
     * @param char $units
     */
    public function __construct($cityId = NULL, $dayLength='5', $units = "m")
    {    
        //set dayLength, cityId and units    
        $this->dayLength = $dayLength;
        $this->cityId = $cityId;
        $this->units  = $units;
    }
    
    /**
     * fetchForecast
     *    
     * @return array forecast
     */
    public function fetchForecast()
    {            
        $this->xmlLocation = "http://xoap.weather.com/weather/local/".$this->cityId."?cc=*&dayf=".$this->dayLength."&link=xoap&prod=xoap&par=".$this->partnerId."&key=".$this->licenseKey."&unit=".$this->units;
        $this->xmlUrl = md5($this->xmlLocation);
        
        //check for cachefile
        $this->xmlString = $this->_fetchCache();
        
        //make new simple XML element from string
        $this->xmlData = new SimpleXMLElement($this->xmlString);
        
        //prepare array                    
        $weatherForeCast = array();
        
        //fetch location data
        if(isset($this->xmlData->loc))
            $weatherForeCast['location'] = $this->_fetchLocationData();
        
        //fetch current conditions
        if(isset($this->xmlData->cc))        
            $weatherForeCast['current_conditions'] = $this->_fetchCurrentConditionsData();
        
        
        //fetch day forecasts        
        if(isset($this->xmlData->dayf))
        {
            $weatherForeCast['last_update_dayforecasts'] = $this->xmlData->dayf->lsup;
            $weatherForeCast['day_forecasts'] = $this->_fetchDayForecastsData();
        }
        
        //return weather forecast
        return $weatherForeCast;
    }
    
    /**
     * _fetchCache
     *    
     * @return string data
     */
    private function _fetchCache()
    {
        $data = false;
        
        //check if cachedir exists, if doesn't exist do API-request and fetch XML, this will lead to huge overhead so make sure the cachedir exists
        if(!is_dir($this->cachePath))
            return $this->_fetchXML($this->xmlLocation);
        
        $cachefilename = $this->cachePath . $this->xmlUrl;
        //check if cache is still up to date
        if (file_exists($cachefilename) && (time() - $this->cacheLifetime < filemtime($cachefilename)))
        {
            $url = $cachefilename;
            $fp = fopen($url,"r");
            while (!feof ($fp))$data .= fgets($fp, 4096);
            fclose ($fp);
        }
        else
        {        
            //make cache
            $data = $this->_fetchXML($this->xmlLocation);    
            $fw = fopen($cachefilename, 'w');
            fwrite($fw, $data);
            fclose($fw);
        }
        
        return $data;        
    }
        
    /**
     * _fetchXML
     *
     * fetch xml data via CURL    
     *
     * @param string $url
     * @return string data
     */
    private function _fetchXML($url)
    {
        $curl = curl_init();
        curl_setopt ($curl, CURLOPT_URL, $url);
        curl_setopt ($curl, CURLOPT_TIMEOUT, 30);
        curl_setopt ($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER, 0);

        $html = curl_exec ($curl);
        if (!$html)        
            throw new Exception('Could not load URL');        
        
        curl_close ($curl);
        return $html;
    }
        
    /**
     * _fetchLocationData
     *
     * fetch location data    
     *
     * @return array locationData
     */
    private function _fetchLocationData()
    {
        $location = $this->xmlData->loc;
        return array(        
            'name'             => $location->dnam,
            'locale_time'    => date("H:i", strtotime($location->tm)),
            'latitude'         => $location->lat,
            'longitude'     => $location->lon,
            'sunrise'         => date("H:i", strtotime($location->sunr)),
            'sunset'         => date("H:i", strtotime($location->suns)),
            'timezone'         => $location->zone
        );
    }
        
    /**
     * _fetchCurrentConditionsData
     *
     * fetch current conditions    
     *
     * @return array currentConditions
     */
    private function _fetchCurrentConditionsData()
    {
        $currentConditions    = $this->xmlData->cc;
        return array(
                'last_update'         => date("Y-m-d H:i:s", strtotime(str_replace('Local Time', '', $currentConditions->lsup))),
                'observation_tower' => $currentConditions->obst,
                'temperature'         => $currentConditions->tmp,
                'feels_like'        => $currentConditions->flik,
                'text'                 => $currentConditions->t,
                'icon'                 => $currentConditions->icon,        
                'bar_value'         => $currentConditions->bar->r,
                'bar_text'             => $currentConditions->bar->d,            
                'wind_speed'         => $currentConditions->wind->s,
                'wind_gust'         => $currentConditions->wind->gust,
                'wind_direction'     => $currentConditions->wind->d,
                'wind_text'         => $currentConditions->wind->t,
                'humidity'             => $currentConditions->hmid,
                'visibility'         => $currentConditions->vis,        
                'uv_index'            => $currentConditions->uv->i,
                'uv_text'             => $currentConditions->uv->t,
                'dew_point'         => $currentConditions->dewp,
                'moon_icon'         => $currentConditions->moon->icon,
                'moon_text'         => $currentConditions->moon->t
            );
    }
        
    /**
     * _fetchDayForecastsData
     *
     * fetch day forecasts    
     *
     * @return array dayForecasts
     */
    private function _fetchDayForecastsData()
    {
        $days = array();
        $dayForecasts = $this->xmlData->dayf;
        foreach($dayForecasts->day as $child)
        {    
            $day = array(
                'index'     => $child->attributes()->d,
                'text'         => $child->attributes()->t,
                'date'         => $child->attributes()->dt,
                'high'         => $child->hi,
                'low'         => $child->low,
                'sunrise'     => date("H:i", strtotime($child->suns)),
                'sunset'     => date("H:i", strtotime($child->sunr))
            );
            foreach($child->part as $part)
            {
                //check if day or night
                $dayNight = ($part->attributes()->p == 'd' ? 'day' : 'night');
                $day[$dayNight] = array(
                    'icon'              => $part->icon,
                    'text'              => $part->t,
                    'wind_speed'      => $part->wind->s,
                    'wind_gust'      => $part->wind->gust,
                    'wind_direction' => $part->wind->d,
                    'wind_text'      => $part->wind->t,
                    'bt'               => $part->bt,
                    'ppcp'              => $part->ppcp,
                    'humidity'          => $part->hmid,
                );                    

            }
            
            $days[] = $day;                
        }
        return $days;
    }

}